package com.jeasonzhao.commons.json;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;

import org.apache.commons.beanutils.PropertyUtils;
import com.jeasonzhao.commons.basic.IntegerPair;
import com.jeasonzhao.commons.basic.IntegerPairHelper;
import com.jeasonzhao.commons.parser.lex.LexToken;
import com.jeasonzhao.commons.parser.lex.LexTokenCollection;
import com.jeasonzhao.commons.utils.Algorithms;
import com.jeasonzhao.commons.utils.DataTypes;
import com.jeasonzhao.commons.parser.lex.LexException;

public abstract class JSONNode implements java.io.Serializable
{
    private static final long serialVersionUID = 1L;
    protected JSONNodeTypes m_nNodeType = JSONNodeTypes.NONE;
    protected LexTokenCollection m_tokens = null;
    protected boolean javaScriptCompatible = true;
    public JSONNode(JSONNodeTypes type)
    {
        super();
        m_nNodeType = type;
    }

    public int amountOfTokens()
    {
        return m_tokens.size();
    }

    public LexToken getToken(int n)
    {
        return n < 0 || m_tokens == null || n >= m_tokens.size() ? null : m_tokens.get(n);
    }

    public boolean isJSONClass()
    {
        return m_nNodeType == JSONNodeTypes.CLASS;
    }

    public boolean isJSONArray()
    {
        return m_nNodeType == JSONNodeTypes.ARRAY;
    }

    public boolean isJSONConstant()
    {
        return m_nNodeType == JSONNodeTypes.CONSTANT;
    }

    public JSONNodeTypes getNodeType()
    {
        return m_nNodeType;
    }

    public String toString()
    {
        return null == m_tokens ? toJSON() : m_tokens.toString();
    }

    protected String getPrefixSpace(int nLevel)
    {
        return Algorithms.repeatString("    ",nLevel);
    }

    public String toJSON()
    {
        return toJSON(true);
    }

    public String toJSON(boolean isFullMode)
    {
        return toJSON(isFullMode,0);
    }

    public JSONNode setJavaScriptCompatible(boolean b)
    {
        this.javaScriptCompatible = b;
        return this;
    }

    protected abstract String toJSON(boolean isFullMode,int nLevel);

    public static JSONNode object2JSONNodeSafe(Object object)
    {
        try
        {
            return object2JSONNode(object);
        }
        catch(JSONException ex)
        {
            ex.printStackTrace();
            if(null == object)
            {
                return null;
            }
            else
            {
                return new JSONConstant(object);
            }
        }
    }

    @SuppressWarnings("rawtypes")
    public static JSONNode object2JSONNode(Object object)
        throws JSONException
    {
        if(null == object)
        {
            return null;
        }
        else if(object instanceof JSONNode)
        {
            return(JSONNode) object;
        }
        else if(object.getClass().isEnum())
        {
            //Get the ordinal of the enum, default implementation
            return new JSONConstant(((Enum) object).ordinal());
        }
        else if(object instanceof IntegerPair)
        {
            return new JSONConstant(IntegerPairHelper.getConstantFieldName((IntegerPair) object));
        }
        else if(DataTypes.isPrimitive(object.getClass()))
        {
            return new JSONConstant(object);
        }
        else if(DataTypes.isArray(object.getClass()))
        {
            JSONArray jsonary = new JSONArray();
            Object[] ary = Algorithms.toArray(object);
            if(null != ary)
            {
                for(Object obj : ary)
                {
                    jsonary.add(object2JSONNode(obj));
                }
            }
            return jsonary;
        }
        else if(java.util.Map.class.isAssignableFrom(object.getClass()))
        {
            JSONClass cls = new JSONClass();
            java.util.Map<?,?> map = (java.util.Map) object;
            for(java.util.Map.Entry<?,?> e : map.entrySet())
            {
                String key = Algorithms.toString(e.getKey());
                if(Algorithms.isValidateSymbolName(key) == false)
                {
                    throw new JSONException("Could not convert \"" + key + "\" to a JSON name");
                }
                cls.set(key,object2JSONNode(e.getValue()));
            }
            return cls;
        }
        else
        {
            return object2JSONNodeSubRoute(object);
        }
    }

    private static JSONClass object2JSONNodeSubRoute(Object obj)
        throws JSONException
    {
        if(null == obj)
        {
            return null;
        }
        try
        {
            JSONClass nodeReturn = new JSONClass();
            PropertyDescriptor[] pds = PropertyUtils.getPropertyDescriptors(obj.getClass());
            for(PropertyDescriptor p : pds)
            {
                if(p.getName().equals("class"))
                {
                    continue;
                }
                if(p.getReadMethod() != null)
                {
                    String name = p.getName();
                    Object value = PropertyUtils.getSimpleProperty(obj,name);
                    nodeReturn.set(name,object2JSONNode(value));
                }
            }
            return nodeReturn;
        }
        catch(JSONException ex)
        {
            throw ex;
        }
        catch(NoSuchMethodException ex)
        {
            throw new JSONException(ex);
        }
        catch(InvocationTargetException ex)
        {
            throw new JSONException(ex);
        }
        catch(IllegalAccessException ex)
        {
            throw new JSONException(ex);
        }
    }
    public static final java.util.ArrayList<java.util.ArrayList<String>> simpleStringParse(String str)
        throws LexException
    {
        return simpleStringParse(str,';');
    }
    public static final java.util.ArrayList<java.util.ArrayList<String>> simpleStringParse(String str,char groupSep)
        throws LexException
    {
        if(Algorithms.isEmpty(str))
        {
            return null;
        }
        JSONTokenizer tk=new JSONTokenizer(str);
        String prevString="";
        java.util.ArrayList<java.util.ArrayList<String>> rows=new java.util.ArrayList<java.util.ArrayList<String>>();
        java.util.ArrayList<String> row=new java.util.ArrayList<String>();
        for(LexToken t : tk.getTokens(false,false))
        {
            if(t.isSpecial() &&t.equals(""+groupSep)==false)
            {
                row.add(prevString);
                prevString="";
            }
            else if(t.isSpecial() &&t.equals(""+groupSep))
            {
                row.add(prevString);
                prevString="";
                rows.add(row);
                row=new java.util.ArrayList<String>();
            }
            else
            {
                prevString+=t.getInitString();
            }
        }
        if(prevString.length()>0)
        {
            row.add(prevString);
        }
        if(row.size()>0)
        {
            rows.add(row);
        }
        return rows;
    }

}
